From cd98204619f25db976fc2b8b99f2c08966aa3fbc Mon Sep 17 00:00:00 2001 From: Carlos Garnacho Date: Sat, 30 Oct 2010 21:48:38 +0200 Subject: [PATCH] Implement widget states as a set of flags gtk_widget_(set|unset|get)_state_flags() has been added, using GtkStateFlags to represent the widget state. GtkStateType API has been implemented on top of the new one. --- docs/reference/gtk/gtk3-sections.txt | 3 + gtk/gtkmarshalers.list | 1 + gtk/gtkwidget.c | 338 ++++++++++++++++++++------- gtk/gtkwidget.h | 9 + 4 files changed, 271 insertions(+), 80 deletions(-) diff --git a/docs/reference/gtk/gtk3-sections.txt b/docs/reference/gtk/gtk3-sections.txt index 2da137cf31..d47480836b 100644 --- a/docs/reference/gtk/gtk3-sections.txt +++ b/docs/reference/gtk/gtk3-sections.txt @@ -4933,6 +4933,9 @@ gtk_widget_is_sensitive gtk_widget_get_state gtk_widget_get_visible gtk_widget_set_visible +gtk_widget_set_state_flags +gtk_widget_unset_state_flags +gtk_widget_get_state_flags gtk_widget_has_default gtk_widget_has_focus gtk_widget_has_grab diff --git a/gtk/gtkmarshalers.list b/gtk/gtkmarshalers.list index 22af46d610..10f0dfd689 100644 --- a/gtk/gtkmarshalers.list +++ b/gtk/gtkmarshalers.list @@ -69,6 +69,7 @@ VOID:ENUM,FLOAT,BOOLEAN VOID:ENUM,INT VOID:ENUM,INT,BOOLEAN VOID:ENUM,BOXED +VOID:FLAGS VOID:INT VOID:INT,BOOLEAN VOID:INT,INT diff --git a/gtk/gtkwidget.c b/gtk/gtkwidget.c index f038d7aea4..a284f84839 100644 --- a/gtk/gtkwidget.c +++ b/gtk/gtkwidget.c @@ -290,15 +290,7 @@ struct _GtkWidgetPrivate * 5 widget states (defined in "gtkenums.h") * so 3 bits. */ - guint state : 3; - - /* The saved state of the widget. When a widget's state - * is changed to GTK_STATE_INSENSITIVE via - * "gtk_widget_set_state" or "gtk_widget_set_sensitive" - * the old state is kept around in this field. The state - * will be restored once the widget gets sensitive again. - */ - guint saved_state : 3; + guint state_flags : 6; guint direction : 2; @@ -389,6 +381,7 @@ enum { REALIZE, UNREALIZE, SIZE_ALLOCATE, + STATE_FLAGS_CHANGED, STATE_CHANGED, PARENT_SET, HIERARCHY_CHANGED, @@ -494,10 +487,16 @@ enum { typedef struct _GtkStateData GtkStateData; +enum { + STATE_CHANGE_REPLACE, + STATE_CHANGE_SET, + STATE_CHANGE_UNSET +}; + struct _GtkStateData { - GtkStateType state; - guint state_restoration : 1; + guint flags : 6; + guint operation : 2; guint parent_sensitive : 1; guint use_forall : 1; }; @@ -1432,6 +1431,8 @@ gtk_widget_class_init (GtkWidgetClass *klass) * * The ::state-changed signal is emitted when the widget state changes. * See gtk_widget_get_state(). + * + * Deprecated: 3.0. Use #GtkWidget::state-flags-changed instead. */ widget_signals[STATE_CHANGED] = g_signal_new (I_("state-changed"), @@ -1443,6 +1444,26 @@ gtk_widget_class_init (GtkWidgetClass *klass) G_TYPE_NONE, 1, GTK_TYPE_STATE_TYPE); + /** + * GtkWidget::state-flags-changed: + * @widget: the object which received the signal. + * @flags: The previous state flags. + * + * The ::state-flags-changed signal is emitted when the widget state + * changes, see gtk_widget_get_state_flags(). + * + * Since: 3.0 + */ + widget_signals[STATE_FLAGS_CHANGED] = + g_signal_new (I_("state-flags-changed"), + G_TYPE_FROM_CLASS (gobject_class), + G_SIGNAL_RUN_FIRST, + G_STRUCT_OFFSET (GtkWidgetClass, state_flags_changed), + NULL, NULL, + _gtk_marshal_VOID__FLAGS, + G_TYPE_NONE, 1, + GTK_TYPE_STATE_FLAGS); + /** * GtkWidget::parent-set: * @widget: the object on which the signal is emitted @@ -3465,8 +3486,6 @@ gtk_widget_init (GtkWidget *widget) priv = widget->priv; priv->child_visible = TRUE; - priv->state = GTK_STATE_NORMAL; - priv->saved_state = GTK_STATE_NORMAL; priv->name = NULL; priv->allocation.x = -1; priv->allocation.y = -1; @@ -6708,37 +6727,34 @@ gtk_widget_get_name (GtkWidget *widget) return G_OBJECT_TYPE_NAME (widget); } -/** - * gtk_widget_set_state: - * @widget: a #GtkWidget - * @state: new state for @widget - * - * This function is for use in widget implementations. Sets the state - * of a widget (insensitive, prelighted, etc.) Usually you should set - * the state using wrapper functions such as gtk_widget_set_sensitive(). - **/ -void -gtk_widget_set_state (GtkWidget *widget, - GtkStateType state) +static void +_gtk_widget_update_state_flags (GtkWidget *widget, + GtkStateFlags flags, + guint operation) { GtkWidgetPrivate *priv; - g_return_if_fail (GTK_IS_WIDGET (widget)); - priv = widget->priv; - if (state == gtk_widget_get_state (widget)) - return; + /* Handle insensitive first, since it is propagated + * differently throughout the widget hierarchy. + */ + if ((flags & GTK_STATE_FLAG_INSENSITIVE) != + (priv->state_flags & GTK_STATE_FLAG_INSENSITIVE)) + gtk_widget_set_sensitive (widget, + operation != STATE_CHANGE_UNSET); - if (state == GTK_STATE_INSENSITIVE) - gtk_widget_set_sensitive (widget, FALSE); - else + flags &= ~(GTK_STATE_FLAG_INSENSITIVE); + + if (flags != 0 || + operation == STATE_CHANGE_REPLACE) { GtkStateData data; - data.state = state; - data.state_restoration = FALSE; + data.flags = flags; + data.operation = operation; data.use_forall = FALSE; + if (priv->parent) data.parent_sensitive = (gtk_widget_is_sensitive (priv->parent) != FALSE); else @@ -6751,6 +6767,141 @@ gtk_widget_set_state (GtkWidget *widget, } } +/** + * gtk_widget_set_state_flags: + * @widget: a #GtkWidget + * @flags: State flags to turn on + * @clear: Whether to clear state before turning on @flags + * + * This function is for use in widget implementations. Turns on flag + * values in the current widget state (insensitive, prelighted, etc.). + * + * It is worth mentioning that any other state than %GTK_STATE_FLAG_INSENSITIVE, + * will be propagated down to all non-internal children if @widget is a + * #GtkContainer, while %GTK_STATE_FLAG_INSENSITIVE itself will be propagated + * down to all #GtkContainer children by different means than turning on the + * state flag down the hierarchy, both gtk_widget_get_state_flags() and + * gtk_widget_is_sensitive() will make use of these. + * + * Since: 3.0 + **/ +void +gtk_widget_set_state_flags (GtkWidget *widget, + GtkStateFlags flags, + gboolean clear) +{ + g_return_if_fail (GTK_IS_WIDGET (widget)); + + if ((!clear && (widget->priv->state_flags & flags) == flags) || + (clear && widget->priv->state_flags == flags)) + return; + + if (clear) + _gtk_widget_update_state_flags (widget, flags, STATE_CHANGE_REPLACE); + else + _gtk_widget_update_state_flags (widget, flags, STATE_CHANGE_SET); +} + +/** + * gtk_widget_unset_state_flags: + * @widget: a #GtkWidget + * @flags: State flags to turn off + * + * This function is for use in widget implementations. Turns off flag + * values for the current widget state (insensitive, prelighted, etc.). + * See gtk_widget_set_state_flags(). + * + * Since: 3.0 + **/ +void +gtk_widget_unset_state_flags (GtkWidget *widget, + GtkStateFlags flags) +{ + g_return_if_fail (GTK_IS_WIDGET (widget)); + + if ((widget->priv->state_flags & flags) == 0) + return; + + _gtk_widget_update_state_flags (widget, flags, STATE_CHANGE_UNSET); +} + +/** + * gtk_widget_get_state_flags: + * @widget: a #GtkWidget + * + * Returns the widget state as a flag set. It is worth mentioning + * that the effective %GTK_STATE_FLAG_INSENSITIVE state will be + * returned, that is, also based on parent insensitivity, even if + * @widget itself is sensitive. + * + * Returns: The state flags for widget + * + * Since: 3.0 + **/ +GtkStateFlags +gtk_widget_get_state_flags (GtkWidget *widget) +{ + GtkStateFlags flags; + + g_return_val_if_fail (GTK_IS_WIDGET (widget), 0); + + flags = widget->priv->state_flags; + + if (!gtk_widget_is_sensitive (widget)) + flags |= GTK_STATE_FLAG_INSENSITIVE; + + return flags; +} + +/** + * gtk_widget_set_state: + * @widget: a #GtkWidget + * @state: new state for @widget + * + * This function is for use in widget implementations. Sets the state + * of a widget (insensitive, prelighted, etc.) Usually you should set + * the state using wrapper functions such as gtk_widget_set_sensitive(). + * + * Deprecated: 3.0. Use gtk_widget_set_state_flags() instead. + **/ +void +gtk_widget_set_state (GtkWidget *widget, + GtkStateType state) +{ + GtkStateFlags flags; + + if (state == gtk_widget_get_state (widget)) + return; + + switch (state) + { + case GTK_STATE_ACTIVE: + flags = GTK_STATE_FLAG_ACTIVE; + break; + case GTK_STATE_PRELIGHT: + flags = GTK_STATE_FLAG_PRELIGHT; + break; + case GTK_STATE_SELECTED: + flags = GTK_STATE_FLAG_SELECTED; + break; + case GTK_STATE_INSENSITIVE: + flags = GTK_STATE_FLAG_INSENSITIVE; + break; + case GTK_STATE_INCONSISTENT: + flags = GTK_STATE_FLAG_INCONSISTENT; + break; + case GTK_STATE_FOCUSED: + flags = GTK_STATE_FLAG_FOCUSED; + break; + case GTK_STATE_NORMAL: + default: + flags = 0; + break; + } + + _gtk_widget_update_state_flags (widget, flags, STATE_CHANGE_REPLACE); +} + /** * gtk_widget_get_state: * @widget: a #GtkWidget @@ -6760,13 +6911,32 @@ gtk_widget_set_state (GtkWidget *widget, * Returns: the state of @widget. * * Since: 2.18 + * + * Deprecated: 3.0. Use gtk_widget_get_state_flags() instead. */ GtkStateType gtk_widget_get_state (GtkWidget *widget) { + GtkStateFlags flags; + g_return_val_if_fail (GTK_IS_WIDGET (widget), GTK_STATE_NORMAL); - return widget->priv->state; + flags = gtk_widget_get_state_flags (widget); + + if (flags & GTK_STATE_FLAG_INSENSITIVE) + return GTK_STATE_INSENSITIVE; + else if (flags & GTK_STATE_FLAG_INCONSISTENT) + return GTK_STATE_INCONSISTENT; + else if (flags & GTK_STATE_FLAG_ACTIVE) + return GTK_STATE_ACTIVE; + else if (flags & GTK_STATE_FLAG_SELECTED) + return GTK_STATE_SELECTED; + else if (flags & GTK_STATE_FLAG_FOCUSED) + return GTK_STATE_FOCUSED; + else if (flags & GTK_STATE_FLAG_PRELIGHT) + return GTK_STATE_PRELIGHT; + else + return GTK_STATE_NORMAL; } /** @@ -7173,17 +7343,19 @@ gtk_widget_set_sensitive (GtkWidget *widget, if (widget->priv->sensitive == sensitive) return; + data.flags = GTK_STATE_FLAG_INSENSITIVE; + if (sensitive) { widget->priv->sensitive = TRUE; - data.state = priv->saved_state; + data.operation = STATE_CHANGE_UNSET; } else { widget->priv->sensitive = FALSE; - data.state = gtk_widget_get_state (widget); + data.operation = STATE_CHANGE_SET; } - data.state_restoration = TRUE; + data.use_forall = TRUE; if (priv->parent) @@ -7192,6 +7364,7 @@ gtk_widget_set_sensitive (GtkWidget *widget, data.parent_sensitive = TRUE; gtk_widget_propagate_state (widget, &data); + if (gtk_widget_is_drawable (widget)) gtk_widget_queue_draw (widget); @@ -7267,6 +7440,7 @@ void gtk_widget_set_parent (GtkWidget *widget, GtkWidget *parent) { + GtkStateFlags parent_flags; GtkWidgetPrivate *priv; GtkStateData data; @@ -7293,14 +7467,17 @@ gtk_widget_set_parent (GtkWidget *widget, g_object_ref_sink (widget); priv->parent = parent; - if (gtk_widget_get_state (parent) != GTK_STATE_NORMAL) - data.state = gtk_widget_get_state (parent); - else - data.state = gtk_widget_get_state (widget); - data.state_restoration = FALSE; + parent_flags = gtk_widget_get_state_flags (parent); + + /* Merge both old state and current parent state, + * We don't want the insensitive flag to propagate + * to the new child though */ + data.flags = parent_flags & ~GTK_STATE_FLAG_INSENSITIVE; + data.flags |= priv->state_flags; + + data.operation = STATE_CHANGE_REPLACE; data.parent_sensitive = (gtk_widget_is_sensitive (parent) != FALSE); data.use_forall = gtk_widget_is_sensitive (parent) != gtk_widget_is_sensitive (widget); - gtk_widget_propagate_state (widget, &data); gtk_widget_reset_rc_styles (widget); @@ -7900,7 +8077,8 @@ gtk_widget_real_style_set (GtkWidget *widget, if (gtk_widget_get_realized (widget) && gtk_widget_get_has_window (widget)) - gtk_style_set_background (priv->style, priv->window, priv->state); + gtk_style_set_background (priv->style, priv->window, + gtk_widget_get_state (widget)); } static void @@ -10371,33 +10549,27 @@ gtk_widget_propagate_state (GtkWidget *widget, GtkStateData *data) { GtkWidgetPrivate *priv = widget->priv; - guint8 old_state = gtk_widget_get_state (widget); - guint8 old_saved_state = priv->saved_state; + GtkStateFlags old_flags = priv->state_flags; + GtkStateType old_state; - /* don't call this function with state==GTK_STATE_INSENSITIVE, - * parent_sensitive==TRUE on a sensitive widget - */ + old_state = gtk_widget_get_state (widget); + if (!priv->parent_sensitive) + old_flags |= GTK_STATE_FLAG_INSENSITIVE; priv->parent_sensitive = data->parent_sensitive; - if (gtk_widget_is_sensitive (widget)) - { - if (data->state_restoration) - priv->state = priv->saved_state; - else - priv->state = data->state; - } - else + switch (data->operation) { - if (!data->state_restoration) - { - if (data->state != GTK_STATE_INSENSITIVE) - priv->saved_state = data->state; - } - else if (gtk_widget_get_state (widget) != GTK_STATE_INSENSITIVE) - priv->saved_state = gtk_widget_get_state (widget); - priv->state = GTK_STATE_INSENSITIVE; + case STATE_CHANGE_REPLACE: + priv->state_flags = data->flags; + break; + case STATE_CHANGE_SET: + priv->state_flags |= data->flags; + break; + case STATE_CHANGE_UNSET: + priv->state_flags &= ~(data->flags); + break; } if (gtk_widget_is_focus (widget) && !gtk_widget_is_sensitive (widget)) @@ -10405,12 +10577,12 @@ gtk_widget_propagate_state (GtkWidget *widget, GtkWidget *window; window = gtk_widget_get_toplevel (widget); + if (window && gtk_widget_is_toplevel (window)) gtk_window_set_focus (GTK_WINDOW (window), NULL); } - if (old_state != gtk_widget_get_state (widget) || - old_saved_state != priv->saved_state) + if (old_flags != gtk_widget_get_state_flags (widget)) { g_object_ref (widget); @@ -10418,6 +10590,7 @@ gtk_widget_propagate_state (GtkWidget *widget, gtk_grab_remove (widget); g_signal_emit (widget, widget_signals[STATE_CHANGED], 0, old_state); + g_signal_emit (widget, widget_signals[STATE_FLAGS_CHANGED], 0, old_flags); if (!priv->shadowed) { @@ -10444,7 +10617,7 @@ gtk_widget_propagate_state (GtkWidget *widget, if (!gtk_widget_is_sensitive (widget)) _gtk_widget_synthesize_crossing (widget, NULL, d->data, GDK_CROSSING_STATE_CHANGED); - else if (old_state == GTK_STATE_INSENSITIVE) + else if (old_flags & GTK_STATE_FLAG_INSENSITIVE) _gtk_widget_synthesize_crossing (NULL, widget, d->data, GDK_CROSSING_STATE_CHANGED); @@ -10456,17 +10629,22 @@ gtk_widget_propagate_state (GtkWidget *widget, } if (GTK_IS_CONTAINER (widget)) - { - data->parent_sensitive = (gtk_widget_is_sensitive (widget) != FALSE); - if (data->use_forall) - gtk_container_forall (GTK_CONTAINER (widget), - (GtkCallback) gtk_widget_propagate_state, - data); - else - gtk_container_foreach (GTK_CONTAINER (widget), - (GtkCallback) gtk_widget_propagate_state, - data); - } + { + data->parent_sensitive = gtk_widget_is_sensitive (widget); + + /* Do not propagate insensitive state further */ + data->flags &= ~(GTK_STATE_FLAG_INSENSITIVE); + + if (data->use_forall) + gtk_container_forall (GTK_CONTAINER (widget), + (GtkCallback) gtk_widget_propagate_state, + data); + else + gtk_container_foreach (GTK_CONTAINER (widget), + (GtkCallback) gtk_widget_propagate_state, + data); + } + g_object_unref (widget); } } diff --git a/gtk/gtkwidget.h b/gtk/gtkwidget.h index 125d315b7a..9515d71310 100644 --- a/gtk/gtkwidget.h +++ b/gtk/gtkwidget.h @@ -225,6 +225,8 @@ struct _GtkWidgetClass GtkAllocation *allocation); void (* state_changed) (GtkWidget *widget, GtkStateType previous_state); + void (* state_flags_changed) (GtkWidget *widget, + GtkStateFlags previous_state_flags); void (* parent_set) (GtkWidget *widget, GtkWidget *previous_parent); void (* hierarchy_changed) (GtkWidget *widget, @@ -578,6 +580,13 @@ void gtk_widget_set_state (GtkWidget *widget, GtkStateType state); GtkStateType gtk_widget_get_state (GtkWidget *widget); +void gtk_widget_set_state_flags (GtkWidget *widget, + GtkStateFlags flags, + gboolean clear); +void gtk_widget_unset_state_flags (GtkWidget *widget, + GtkStateFlags flags); +GtkStateFlags gtk_widget_get_state_flags (GtkWidget *widget); + void gtk_widget_set_sensitive (GtkWidget *widget, gboolean sensitive); gboolean gtk_widget_get_sensitive (GtkWidget *widget); -- 2.30.2